#include <iostream>
#include <opencv2/opencv.hpp>
#include <random>

using namespace std;
using namespace cv;

Mat img = Mat::zeros(Size(600, 400), CV_32F);
Point2f Points[4];
Point2f Dv[3];
Point2f Dipart;

void Random_point()
{// 随机生成四点，满足三条线段前后两夹角 一个锐角一个钝角；首尾射线不相交
    random_device rd;
    unsigned int seed = rd();
    std::minstd_rand0 gen(seed);

    Points[0].x = gen()%600;
    Points[0].y = gen()%400;
    Points[1].x = gen()%600;
    Points[1].y = gen()%400;
    Dv[0] = Points[0] - Points[1];
    Dv[0] /= norm(Dv[0]);
    bool oba = 0;
    do
    {
        Points[2].x = gen()%600;
        Points[2].y = gen()%400;
        Dv[1] = Points[1] - Points[2];
        Dv[1] /= norm(Dv[1]);
        oba = (Dv[0].dot(Dv[1]) >= 0);
    } while (oba);
    // 如果是钝角，继续生成P2,直到生成锐角
    bool aca = 0;
    bool exper = 0;
    do
    {
        Points[3].x = gen()%600;
        Points[3].y = gen()%400;
        Dv[2] = Points[2] - Points[3];
        Dv[2] /= norm(Dv[2]);
        float T = Dv[1].dot(Dv[2]);
        aca = (Dv[1].dot(Dv[2]) <= 0);// 锐角判断
        if ((Dv[0].cross(Dv[1]) < 0))
            exper = (Dv[2].cross(Dv[0]) < 0);// 射线相交判断
        else
            exper = (Dv[2].cross(Dv[0]) > 0);// 射线相交判断
    } while (aca || exper);
    Dipart = Dv[1] - Dv[0];
    Dipart /= norm(Dipart);
}

void distanceFieldsigel(Point2f fp, Point2f sp, Mat &image)
{// 单线段生成距离向量场
    Point2f direct = fp - sp; 
    direct /= norm(direct);
    for (int j = 0; j < 600; j++)
    {
        for (int i = 0; i < 400; i++)
        {
            Point2f p = Point2f(j, i);
            Point2f f2p = p - fp;
            Point2f s2p = p - sp;
            bool over_f = (f2p.dot(direct) >= 0);
            bool over_s = (s2p.dot(direct) <= 0);
            bool CCW = (f2p.cross(direct) >= 0);
            float dist = 0;
            if (!over_f && !over_s)
            {
                float t1=norm(f2p);
                float t2=direct.dot(f2p);
                dist = sqrt(pow(norm(f2p), 2) - pow(direct.dot(f2p), 2));
            }
            else if (over_f)
                dist = norm(f2p);
            else
                dist = norm(s2p);
            if (CCW)
                image.at<float>(p) = -dist;
            else
                image.at<float>(p) = dist;
        }
    }
}

void distanceField(Point2f *P, Mat &image_heat)
{// 三线段生成距离向量场
    Mat image[3] = {
        Mat::zeros(cv::Size(600, 400), CV_32F),
        Mat::zeros(cv::Size(600, 400), CV_32F),
        Mat::zeros(cv::Size(600, 400), CV_32F)
    };
    // 各自生成距离向量场
    distanceFieldsigel(P[0], P[1], image[0]);
    distanceFieldsigel(P[1], P[2], image[1]);
    distanceFieldsigel(P[2], P[3], image[2]);
    for (int j = 0; j < 600; j++)
    {
        for (int i = 0; i < 400; i++)
        {
            Point2f p = Point2f(j, i);
            Point2f p21 = p - Points[1];
            bool area = (p21.cross(Dv[1]) * p21.cross(Dipart) >0) && ( Dipart.cross(Dv[1]) * p21.cross(Dipart) >0);
            float p2d[] = {abs(image[0].at<float>(p)), abs(image[1].at<float>(p)), abs(image[2].at<float>(p))};
            int minPos = min_element(p2d,p2d+3) - p2d;
            switch (minPos)
            {// 取像素距离最小值作为最终图像素值
            case 0:
                if (area)
                    image_heat.at<float>(p) = image[1].at<float>(p);
                else
                    image_heat.at<float>(p) = image[0].at<float>(p);
                break;
            case 1:
                image_heat.at<float>(p) = image[1].at<float>(p);
                break;
            case 2:
                image_heat.at<float>(p) = image[2].at<float>(p);
                break;
            default:
                break;
            }
        }
    }
}

void viewazation(Point2f *P,Mat imageg)
{
    double maxt = 0;
    double mint = 0;
    minMaxLoc(imageg, &mint, &maxt);
    imageg = (imageg - mint);
    Mat heatmap_8U;
    imageg.convertTo(heatmap_8U, CV_8U, 255/(maxt - mint), 0.0);
    
    // 应用颜色映射
    Mat colored_heatmap;
    applyColorMap(heatmap_8U, colored_heatmap, COLORMAP_JET);
    
    // 显示热图
    line(colored_heatmap, P[0], P[1], Scalar(255, 255, 255), 2);
    line(colored_heatmap, P[1], P[2], Scalar(255, 255, 255), 2);
    line(colored_heatmap, P[2], P[3], Scalar(255, 255, 255), 2);
    imshow("Heatmap", colored_heatmap);
    imwrite("实例图样.jpeg", colored_heatmap);
}

int main()
{
    Random_point();
    
    distanceField(Points, img);
    viewazation(Points, img);
    waitKey(0);

    return 0;
}